﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Routing;
using System.Configuration;
using Microsoft.Practices.Unity;
using Vit.Common;
using Vit.Common.Providers;
using Vit.Common.Providers.DefaultProviders;
using Vit.Web;
using Vit.Web.Models;
using Vit.Web.Providers;
using Vit.Mvc.Filters;
using Vit.Mvc.Providers;
using Vit.Mvc.Modules;

namespace Vit.Mvc
{
    public class UnityMvcWebApplication : UnityWebApplication
    {
        protected override void Application_Start(object sender, EventArgs e)
        {
            base.Application_Start(sender, e);
            Reload();
            foreach (AreaBase area in Areas)
            {
                area.OnApplicationStart(sender as HttpApplication);
            }
        }
        protected override void Application_End(object sender, EventArgs e)
        {
            foreach (AreaBase area in Areas)
            {
                area.OnApplicationEnd(sender as HttpApplication);
            }
            Unload();
        }
        protected override void Application_Error(object sender, EventArgs e)
        {
            foreach (AreaBase area in Areas)
            {
                area.OnApplicationError(sender as HttpApplication);
            }
        }
        protected override void Application_BeginRequest(object sender, EventArgs e)
        {
            foreach (AreaBase area in Areas)
            {
                area.OnBeginRequest(sender as HttpApplication);
            }
        }
        protected override void Application_EndRequest(object sender, EventArgs e)
        {
            foreach (AreaBase area in Areas)
            {
                area.OnEndRequest(sender as HttpApplication);
            }
        }
        protected override void Session_Start(object sender, EventArgs e)
        {
            foreach (AreaBase area in Areas)
            {
                area.OnSessionStart(sender as HttpApplication);
            }
        }
        protected override void Session_End(object sender, EventArgs e)
        {
            foreach (AreaBase area in Areas)
            {
                area.OnSessionEnd(sender as HttpApplication);
            }
        }

        protected override IUnityContainer SetupUnityContainer(IUnityContainer parentContainer)
        {
            parentContainer
                .RegisterInstance(RouteTable.Routes)
                .RegisterInstance(ModelBinders.Binders)
                .RegisterInstance(ViewEngines.Engines)
                .RegisterInstance(HostingEnvironment.VirtualPathProvider)
                ;

            foreach (ConnectionStringSettings connectionString in ConfigurationManager.ConnectionStrings)
                parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);

            IUnityContainer childContainer = parentContainer.CreateChildContainer();

            childContainer.RegisterInstance(childContainer);

            return childContainer;
        }

        protected virtual void Reload()
        {
            RegisterServices();

            if (!UnityContainer.IsRegistered<IFilterRegistry>())
            {
                UnityContainer.RegisterType<IFilterRegistry, FilterRegistry>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<IControllerFactory>())
            {
                UnityContainer.RegisterType<IControllerFactory, UnityControllerFactory>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<IActionInvoker>())
            {
                UnityContainer.RegisterType<IActionInvoker, UnityControllerActionInvoker>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<IViewEngine>())
            {
                UnityContainer.RegisterType<IViewEngine, UnityViewEngine>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<INavigationProvider>())
            {
                UnityContainer.RegisterType<INavigationProvider, XmlNavigationProvider>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<IHtmlRenderProvider>())
            {
                UnityContainer.RegisterInstance<IHtmlRenderProvider>("DefaultHtmlRenderProvider", new DefaultHtmlRenderProvider(), new ContainerControlledLifetimeManager());
            }

            if (UnityContainer.IsRegistered<ModelMetadataProvider>())
            {
                ModelMetadataProviders.Current = UnityContainer.Resolve<ModelMetadataProvider>();
            }
            else
            {
                ModelMetadataProviders.Current = new ViewModelMetadataProvider();
            }

            ModelValidatorProviders.Providers.Clear();
            ModelValidatorProviders.Providers.Add(new LocalizedModelValidatorProvider());
            ControllerBuilder.Current.SetControllerFactory(UnityContainer.Resolve<IControllerFactory>());
            ViewEngines.Engines.Clear();
            IViewEngine viewEngine = UnityContainer.Resolve<IViewEngine>();
            ViewEngines.Engines.Add(viewEngine);

            RouteCollection routes = UnityContainer.Resolve<RouteCollection>();
            IFilterRegistry filterRegistry = UnityContainer.Resolve<FilterRegistry>();
            ModelBinderDictionary modelBinders = UnityContainer.Resolve<ModelBinderDictionary>();
            routes.Clear();
            filterRegistry.Clear();
            modelBinders.Clear();

            filterRegistry.Add(Enumerable.Empty<IFilterCriteria>(), typeof(AntiForgeryAuthorizationFilter));

            // load areas by reflection
            LoadAreas();

            RegisterIgnoreRoutes(routes);

            // start the module and plugins if any.
            IEnumerable<AreaBase> areas = UnityContainer.ResolveAll<AreaBase>();

            foreach (AreaBase area in areas)
            {
                area.RegisterWithContainer(UnityContainer);
                area.Initialize();
                area.RegisterFilters(filterRegistry);
                area.RegisterModelBinders(modelBinders);

                if (viewEngine is VirtualPathProviderViewEngine)
                {
                    area.RegisterViewLocationFormats((VirtualPathProviderViewEngine)viewEngine);
                }

                RegisterAreaRegisterContext(area, routes);

                UnityContainer.RegisterInstance(area.AreaName, area);
            }

            RegisterGlobalRoutes(routes);
        }

        protected virtual void RegisterServices()
        {
            if(!UnityContainer.IsRegistered<IBasicDataProvider>())
            {
                UnityContainer.RegisterType<IBasicDataProvider, DefaultBasicDataProvider>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<ICacheProvider>())
            {
                UnityContainer.RegisterType<ICacheProvider, DefaultCacheProvider>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<IHelpProvider>())
            {
                UnityContainer.RegisterType<IHelpProvider, DefaultHelpProvider>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<ILocalizeProvider>())
            {
                UnityContainer.RegisterType<ILocalizeProvider, DefaultLocalizeProvider>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<ILogProvider>())
            {
                UnityContainer.RegisterType<ILogProvider, DefaultLogProvider>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<ISettingProvider>())
            {
                UnityContainer.RegisterType<ISettingProvider, DefaultSettingProvider>(new ContainerControlledLifetimeManager());
            }

            if (!UnityContainer.IsRegistered<IMembershipProvider>())
            {
                UnityContainer.RegisterType<IMembershipProvider, DefaultMembershipProvider>(new ContainerControlledLifetimeManager());
            }
            if (!UnityContainer.IsRegistered<IPortalProvider>())
            {
                UnityContainer.RegisterType<IPortalProvider, DefaultPortalProvider>(new ContainerControlledLifetimeManager());
            }

            RegisterModules();
        }

        protected virtual void Unload()
        {
            IEnumerable<AreaBase> modules = UnityContainer.ResolveAll<AreaBase>();
            foreach (AreaBase module in modules)
                module.Unload();
        }

        protected virtual void LoadAreas()
        {
            string path = HttpContext.Current.Server.MapPath("~/bin");
            DirectoryInfo dir = new DirectoryInfo(path);
            Type areaType = typeof(AreaBase);
            foreach (FileInfo fi in dir.GetFiles("*.dll"))
            {
                Assembly asm = Assembly.LoadFile(fi.FullName);
                foreach (Type type in asm.GetTypes().Where(o => areaType.IsAssignableFrom(o)))
                {
                    AreaBase area = Activator.CreateInstance(type) as AreaBase;
                    UnityContainer.RegisterInstance<AreaBase>(area.AreaName, area);
                }
            }
        }

        protected virtual void RegisterModules()
        {
            BannerModule banner = new BannerModule();
            UnityContainer.RegisterInstance<IWebModule>(banner.ModuleID.ToString(), banner, new ContainerControlledLifetimeManager());

            ChildActionModule childAction = new ChildActionModule();
            UnityContainer.RegisterInstance<IWebModule>(childAction.ModuleID.ToString(), childAction, new ContainerControlledLifetimeManager());
        }

        protected virtual void RegisterIgnoreRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("{resource}.js/{*pathInfo}");
            routes.IgnoreRoute("{resource}.css/{*pathInfo}");
            routes.IgnoreRoute("{resource}.gif/{*pathInfo}");
            routes.IgnoreRoute("{resource}.jpg/{*pathInfo}");
            routes.IgnoreRoute("{resource}.png/{*pathInfo}");
            routes.IgnoreRoute("{resource}.ico/{*pathInfo}");
            routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });
        }

        protected virtual void RegisterGlobalRoutes(RouteCollection routes)
        {
            routes.MapRoute("Default", "{controller}/{action}/{id}",
                new { controller = "Page", action = "Visit", id = UrlParameter.Optional }
                );
            routes.MapRoute("DynamicData", "dd/{database}/{table}/{action}/{id}",
               new { controller = "DynamicData", action = "Index", id = UrlParameter.Optional }
               );
        }

        private void RegisterAreaRegisterContext(AreaBase module, RouteCollection routes)
        {
            AreaRegistrationContext context = new AreaRegistrationContext(module.AreaName, routes, null);
            string str = module.GetType().Namespace;
            if (str != null)
            {
                context.Namespaces.Add(str + ".*");
            }
            module.RegisterArea(context);
        }

        public IEnumerable<AreaBase> Areas
        {
            get
            {
                return UnityContainer.ResolveAll<AreaBase>();
            }
        }

        public IUnityContainer UnityContainer
        {
            get
            {
                return WebUnity.Current.UnityContainer;
            }
        }
    }
}
